#pragma once

#include <vector>
#include <set>
#include "OurModel.h"
#include "Point.h"
using namespace std;

class CMemDC;
class Point;
class Matrix3D;
class OurModel;
class CPen;

class OurObject
{
public:
	OurObject(OurModel* parent);
	~OurObject(void);
	void AddPoint(double dbX,double dbY,double dbZ);
	bool AddFace(vector<unsigned>& FacesIndex);
	void AddVertexNormal (unsigned vertex, double x, double y, double z);
	void ModifyFaceNormal (unsigned face_idx, double x, double y, double z);
	void paint(CMemDC &memDC,bool bDrawBoundingBox = false,bool bDrawAxes = false, bool bDrawFaceNormals = false,
		bool bDrawVertexNormals = false);
	void MultiplyWithMatrix(const Matrix3D& m);
	void reset();
	void SetColor(int nWireColor);
	
	void AddFaces(
		multiset<pair<pair<OurObject*,unsigned>,vector<Matrix3D*> >,
		OurModel::FacesetYComparator>& Faces,
		int& nScreenYmin,
		int& nScreenYmax,
		int& nScreenXmin,
		int& nScreenXmax,
		double& dbScreenZMax,
		double& dbScreenZMin);
	int GetMinY();
	int GetMaxY();
	int GetMinX();
	int GetMaxX();
	double GetMaxZ();
	double GetMinZ();
	void BackFaceCulling(bool bIsOn,bool bNormalSwitchDirection);
	void CalculateVertexNormals();
	void AddTriangulates();
	bool IsBackFace(unsigned index);
	void SetKa(vector<int>& Ka);
	void SetKd(vector<int>& Kd);
	void SetKs(vector<int>& Ks);
	void SetN(int nN);
	void SetAmbientIntensity(double dbAmbientIntensity);
	Matrix3D& GetAmbientIntensity();
	/*int CalculateColor(Matrix3D& ViewVector,
					   Matrix3D& LightVector,					
					   Matrix3D& SurfaceNormal,
					   Matrix3D& AmbientIntensity,
					   vector<Matrix3D>& LightSourcesIntensity);*/
	vector<Matrix3D> GetColorOfVertices(unsigned FaceIndex);
	void ColorPerFace(bool bIsTrue);
	void ColorPerVertex(bool bIsTrue);
	void SetVertexColor(unsigned index, int nR, int nG, int nB);
	void SetFaceColor(unsigned index, int nR, int nG, int nB);
	bool IsColorPerFace();
	bool IsColorPerVertex();
	Matrix3D GetKa(unsigned PolygonIndex);
	Matrix3D GetKd(unsigned PolygonIndex);
	Matrix3D GetKs();
	int GetN();
	Matrix3D GetVertexNormal(unsigned index);
	Matrix3D GetPolygonNormal(unsigned index);

	void SetFlatColorOfFaces(
		vector<pair<Matrix3D,bool> >& LightSources,
		vector<Matrix3D>& LightSourcesIntensity,
		Matrix3D& AmbientIntensity,
		Matrix3D& ViewVector,
		double AmbientFactor,
		double DiffuseFactor,
		double SpecularFactor,
		int nCosineFactor);

	void SetGouraudColorsForVertices(
		vector<pair<Matrix3D,bool> >& LightSources,
		vector<Matrix3D>& LightSourcesIntensity,
		Matrix3D& AmbientIntensity,
		Matrix3D& ViewVector,
		double AmbientFactor,
		double DiffuseFactor,
		double SpecularFactor,
		int nCosineFactor);

	Matrix3D OurObject::GetFlatColor(unsigned FaceIndex);
	Matrix3D OurObject::GetGouraudColor(unsigned VertexIndex);

	static Matrix3D CalculateColor(
		Matrix3D& CenterOfFace,
		Matrix3D& Ka,
		Matrix3D& Kd,
		Matrix3D& Ks,
		Matrix3D& ViewVector,
		vector<pair<Matrix3D,bool> >& LightVectors,
		Matrix3D& SurfaceNormal,
		Matrix3D& AmbientIntensity,
		vector<Matrix3D>& LightSourcesIntensity,
		double nShininess,
		double AmbientFactor,
		double DiffuseFactor,
		double SpecularFactor,
		double nCosineFactor);

	Matrix3D CalculateCenterOfFace(vector<unsigned>& Face);
	unsigned GetVertexIndex(unsigned FaceIndex,unsigned indexInFace);

private:
	void DrawLine(CMemDC &memDC,
				  const Matrix3D& FromPoint,
				  const Matrix3D& ToPoint);
	void PaintBoundingBox(CMemDC &memDC);
	void PaintAxes(CMemDC &memDC);
	void PaintFaceNormals (CMemDC &memDC);
	void PaintVertexNormals (CMemDC &memDC);
	// not used, as we're doing it on the fly on every addition of a Face.
	void ComputeFaceNormals (void);
	Matrix3D GetFaceColor(unsigned index);

private:
	vector<Matrix3D> m_PointsVector;
	vector< vector<unsigned> > m_FacesIndex;
	vector< vector<unsigned> > m_TriangulatedFacesIndex;
	vector<Matrix3D> m_TransformedPoints;

	vector<Matrix3D> m_BoundingBoxPoints;
	vector<Matrix3D> m_TransformedBoundingBoxPoints;

	typedef pair<Matrix3D,Matrix3D> FaceNormal_t;
	vector< FaceNormal_t > m_FaceNormals;
	vector< FaceNormal_t > m_TransformedFaceNormals;
	vector< unsigned > m_TriangulatedFaceNormals;
	
	Matrix3D m_CenterOfMass;
	Matrix3D m_TransformedCenterOfMass;

	vector<Matrix3D> m_AxesBounds;
	vector<Matrix3D> m_TransformedAxesBounds;

	OurModel* m_parent;

	int m_nWireColor;

	vector < pair<unsigned, Matrix3D> > m_VertexNormals;
	vector < pair<unsigned, Matrix3D> > m_TransformedVertexNormals;

	static const int ms_NormalSize = 1;

	bool m_bBackFaceCullingOn;
	bool m_bNormalSwitchDirection;

	bool m_bColorPerVertex;
	bool m_bColorPerFace;

	vector<Matrix3D> m_VertexColors;
	vector<Matrix3D> m_FaceColors;
	//vector<unsigned> m_TrianglesColorsIndex;
	vector<Matrix3D> m_GouraudVertexColor;
	vector<Matrix3D> m_FlatFaceColor;

	//the three color constants, they are stored in the following format
	//(KR 0 0 0)
	//(0 KG 0 0)
	//(0 0 KB 0)
	//(0 0 0 1)
	//this way we can multiply intensities by using matrix multiplication 
	Matrix3D m_Ka;
	Matrix3D m_Kd;
	Matrix3D m_Ks;
	Matrix3D m_AmbientIntensity;
	
	//shininess constant
	int m_nN;


};
